home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 1 / ETO Development Tools 1.iso / Tools - Objects / MacApp / MacApp 2.0 CD Release / MacApp 2.0 (Many Libraries) / Libraries / UMacApp.TDocument.p < prev    next >
Encoding:
Text File  |  1990-03-27  |  43.1 KB  |  1,736 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
  3. { UMacApp.TDocument.p }
  4. { Copyright © 1984-1990 by Apple Computer Inc.    All rights reserved. }
  5.  
  6. {--------------------------------------------------------------------------------------------------}
  7. {$S MAWriteFile}
  8.  
  9. PROCEDURE TSaveDocCommand.DoIt;
  10.  
  11.     BEGIN
  12.     fChangedDocument.Save(fCmdNumber,
  13.     {askForFilename:} NOT fChangedDocument.fSaveExists | (fCmdNumber <> cSave),
  14.     {makingCopy:} fCmdNumber = cSaveCopy);
  15.     END;
  16.  
  17. {--------------------------------------------------------------------------------------------------}
  18. {$S MASelCommand}
  19.  
  20. PROCEDURE TSaveDocCommand.ISaveDocCommand(itsCmdNumber: CmdNumber;
  21.                                           itsDocument: TDocument);
  22.  
  23.     BEGIN
  24.     INoChangesCommand(itsCmdNumber, itsDocument, NIL, NIL);
  25.     END;
  26.  
  27. {--------------------------------------------------------------------------------------------------}
  28. {$S MAFields}
  29.  
  30. PROCEDURE TSaveDocCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  31.                                                      fieldAddr: Ptr;
  32.                                                      fieldType: INTEGER)); OVERRIDE;
  33.  
  34.     BEGIN
  35.     DoToField('TSaveDocCommand', NIL, bClass);
  36.     INHERITED Fields(DoToField);
  37.     END;
  38.  
  39. {--------------------------------------------------------------------------------------------------}
  40. {$S MAReadFile}
  41.  
  42. PROCEDURE TRevertDocCommand.DoIt;
  43.  
  44.     VAR
  45.         name:                Str255;
  46.         fi:                 FailInfo;
  47.  
  48.     PROCEDURE HdlRevertCmd(error: OSErr;
  49.                            message: LONGINT);
  50.  
  51.         BEGIN
  52.         fChangedDocument.ShowReverted;                    { make sure screen is updated }
  53.         END;
  54.  
  55.     BEGIN
  56.     name := fChangedDocument.fTitle^^;
  57.     ParamText(name, '', '', '');
  58.     IF MacAppAlert(phRevert, NIL) = kYesButton THEN     {!!! This should be programatically
  59.                                                          defeatable }
  60.         BEGIN
  61.         CatchFailures(fi, HdlRevertCmd);
  62.         fChangedDocument.Revert;
  63.         Success(fi);
  64.         fChangedDocument.ShowReverted;
  65.         END;
  66.     END;
  67.  
  68. {--------------------------------------------------------------------------------------------------}
  69. {$S MASelCommand}
  70.  
  71. PROCEDURE TRevertDocCommand.IRevertDocCommand(itsCmdNumber: CmdNumber;
  72.                                               itsDocument: TDocument);
  73.  
  74.     BEGIN
  75.     INoChangesCommand(itsCmdNumber, itsDocument, NIL, NIL);
  76.     END;
  77.  
  78. {--------------------------------------------------------------------------------------------------}
  79. {$S MAFields}
  80.  
  81. PROCEDURE TRevertDocCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  82.                                                        fieldAddr: Ptr;
  83.                                                        fieldType: INTEGER)); OVERRIDE;
  84.  
  85.     BEGIN
  86.     DoToField('TRevertDocCommand', NIL, bClass);
  87.     INHERITED Fields(DoToField);
  88.     END;
  89.  
  90. {--------------------------------------------------------------------------------------------------}
  91. {$S MAOpen}
  92.  
  93. PROCEDURE TDocument.IDocument(itsFileType, itsCreator: OSType;
  94.                               usesDataFork, usesRsrcFork: BOOLEAN;
  95.                               keepsDataOpen, keepsRsrcOpen: BOOLEAN);
  96.  
  97.     VAR
  98.         fi:                 FailInfo;
  99.  
  100.     PROCEDURE HdlIDocument(error: OSErr;
  101.                            message: LONGINT);
  102.  
  103.         BEGIN
  104.         Free;
  105.         END;
  106.  
  107.     BEGIN
  108.     fWindowList := NIL;
  109.     fViewList := NIL;
  110.     fPrintInfo := NIL;
  111.     fDocPrintHandler := NIL;
  112.  
  113.     fDataRefnum := kNoFileRefnum;
  114.     fRsrcRefnum := kNoFileRefnum;
  115.  
  116.     fTitle := NIL;
  117.     fPrintInfo := NIL;
  118.     fSavePrintInfo := FALSE;
  119.     fSharePrintInfo := TRUE;
  120.  
  121.     fVolRefNum := 0;
  122.     fReopenAlert := TRUE;
  123.     fSaveExists := FALSE;
  124.     fCommitOnSave := TRUE;
  125.     fModDate := 0;
  126.  
  127.     fFileType := itsFileType;
  128.     fCreator := itsCreator;
  129.  
  130.     fUsesDataFork := usesDataFork;
  131.     fUsesRsrcFork := usesRsrcFork;
  132.     fDataOpen := keepsDataOpen;
  133.     fRsrcOpen := keepsRsrcOpen;
  134.  
  135.     {$IFC qDebug}
  136.     IF fDataOpen & (NOT fUsesDataFork) THEN
  137.         BEGIN
  138.         Writeln('In TDocument.IDocument:  fDataOpen AND NOT fUsesDataFork;');
  139.         Writeln('In TDocument.IDocument:  fUsesDataFork := TRUE;');
  140.         fUsesDataFork := TRUE;
  141.         END;
  142.     IF fRsrcOpen & (NOT fUsesRsrcFork) THEN
  143.         BEGIN
  144.         Writeln('In TDocument.IDocument:  fRsrcOpen AND NOT fUsesRsrcFork;');
  145.         Writeln('In TDocument.IDocument:  fUsesRsrcFork := TRUE;');
  146.         fUsesRsrcFork := TRUE;
  147.         END;
  148.     {$ENDC}
  149.  
  150.     IF keepsDataOpen | keepsRsrcOpen THEN
  151.         fSaveInPlace := sipNever
  152.     ELSE
  153.         fSaveInPlace := sipAskUser;
  154.  
  155.     fDataPerm := fsRdPerm;
  156.     fRsrcPerm := fsRdPerm;
  157.  
  158.     IEvtHandler(gApplication);
  159.  
  160.     CatchFailures(fi, HdlIDocument);
  161.  
  162.     fChangeCount := 0;
  163.  
  164.     fTitle := NewString('');
  165.     FailNil(fTitle);
  166.  
  167.     fWindowList := NewList;
  168.     {$IFC qDebug}
  169.     fWindowList.SetEltType('TWindow');
  170.     {$ENDC}
  171.  
  172.     fViewList := NewList;
  173.     {$IFC qDebug}
  174.     fViewList.SetEltType('TView');
  175.     {$ENDC}
  176.  
  177.     Success(fi);
  178.     END;
  179.  
  180. {--------------------------------------------------------------------------------------------------}
  181. {$S MAClose}
  182.  
  183. PROCEDURE TDocument.Free; OVERRIDE;
  184.  
  185.     BEGIN
  186.     gApplication.DeleteDocument(SELF);
  187.  
  188.     FreeFile;
  189.  
  190.     fWindowList := FreeListIfObject(fWindowList);
  191.     fViewList := FreeListIfObject(fViewList);
  192.  
  193.     IF fSharePrintInfo THEN
  194.         fPrintInfo := DisposeIfHandle(fPrintInfo);
  195.     fPrintInfo := NIL;    { Always drop my reference }
  196.  
  197.     Handle(fTitle) := DisposeIfHandle(fTitle);
  198.  
  199.     INHERITED Free;
  200.     END;
  201.  
  202. {--------------------------------------------------------------------------------------------------}
  203. {$S MAOpen}
  204.  
  205. PROCEDURE TDocument.AddView(aView: TView);
  206.  
  207.     BEGIN
  208.     { Protect against double installation and keep in synch with window list }
  209.  
  210.     IF (fViewList <> NIL) & (fViewList.GetSameItemNo(aView) = 0) THEN
  211.         fViewList.Insert(aView);
  212.  
  213.     IF (fWindowList <> NIL) & member(aView, TWindow) THEN
  214.         IF fWindowList.GetSameItemNo(aView) = 0 THEN
  215.             fWindowList.Insert(aView);
  216.     END;
  217.  
  218. {--------------------------------------------------------------------------------------------------}
  219. {$S MAWriteFile}
  220.  
  221. PROCEDURE TDocument.AboutToSave(itsCmd: CmdNumber;
  222.                                 VAR newName: Str255;
  223.                                 VAR newVolRefnum: INTEGER;
  224.                                 VAR makingCopy: BOOLEAN);
  225.  
  226.     BEGIN
  227.     END;
  228.  
  229. {--------------------------------------------------------------------------------------------------}
  230. {$S MAOpen}
  231.  
  232. PROCEDURE TDocument.AddWindow(aWindow: TWindow);
  233.  
  234.     BEGIN
  235.     { Protect against double installation and keep in synch with window list }
  236.     IF (fWindowList <> NIL) & (fWindowList.GetSameItemNo(aWindow) = 0) THEN { doesn't already exist in
  237.                                                                            list }
  238.         fWindowList.Insert(aWindow);
  239.  
  240.     IF (fViewList <> NIL) & (fViewList.GetSameItemNo(aWindow) = 0) THEN { ??? should we only have one
  241.                                                                        list now… maybe created on
  242.                                                                        demand? (post 2.0) }
  243.         fViewList.Insert(aWindow);
  244.     END;
  245.  
  246. {--------------------------------------------------------------------------------------------------}
  247. {$S MAFile}
  248.  
  249. PROCEDURE TDocument.CheckDiskFile(rsrcId, rsrcIndex: INTEGER;
  250.                                   reverting: BOOLEAN);
  251.  
  252.     VAR
  253.         err:                OSErr;
  254.         name:                Str255;
  255.         s:                    Str255;
  256.  
  257.     BEGIN
  258.     err := DiskFileChanged(reverting);                    {don't care about the file type if saving}
  259.     IF err = errFileChanged THEN
  260.         BEGIN
  261.         name := fTitle^^;
  262.         GetIndString(s, rsrcId, rsrcIndex);
  263.         ParamText(name, s, '', '');
  264.         IF MacAppAlert(phFileChanged, NIL) = cancel THEN {!!! This should be programatically
  265.                                                           defeatable }
  266.             Failure(noErr, msgCancelled);
  267.         END
  268.         {if reverting then we signal an error}
  269.     ELSE IF (err <> noErr) & reverting THEN
  270.         Failure(err, 0);
  271.     END;
  272.  
  273. {--------------------------------------------------------------------------------------------------}
  274. {$S MAClose}
  275.  
  276. PROCEDURE TDocument.CloseView(aView: TView);
  277.  
  278.     FUNCTION OpenWindowCount: ArrayIndex;
  279.     
  280.         VAR
  281.             openDocWindows: integer;
  282.     
  283.         PROCEDURE CountOpenWindows(aWindow: TWindow);
  284.     
  285.             BEGIN
  286.             IF aWindow.IsShown THEN
  287.                 openDocWindows := openDocWindows + 1;
  288.             END;
  289.  
  290.         BEGIN
  291.         { See how many open windows this document has }
  292.         openDocWindows := 0;
  293.         ForAllWindowsDo(CountOpenWindows);
  294.         OpenWindowCount := openDocWindows;
  295.         END;
  296.  
  297.     BEGIN
  298.     IF aView.fDocument = SELF THEN                             { free window }
  299.         BEGIN
  300.         { !!! Yuch!, of course we should be able to ASK any view if it would like to close
  301.         its associated document and maybe even what doc it would like to close.
  302.         Some other time… }
  303.         IF (Member(aView, TWindow) & (TWindow(aView).fClosesDocument) | (OpenWindowCount <= 1)) THEN
  304.             Close { The view will be closed and freed as a side effect }
  305.         ELSE
  306.             aView.Close;
  307.         END;
  308.     END;
  309.  
  310.  
  311. {--------------------------------------------------------------------------------------------------}
  312. {$S MAClose}
  313.  
  314. PROCEDURE TDocument.Close;
  315.  
  316.     VAR
  317.         lastCommand:        TCommand;
  318.         poseResult:            INTEGER;
  319.         changeCount:        LONGINT;
  320.  
  321.     {Must never be called for a document related to a view in the Clipboard.
  322.         Why is this???}
  323.  
  324.     PROCEDURE CloseAWindow(aWindow: TWindow);
  325.  
  326.         BEGIN
  327.         aWindow.Close
  328.         END;
  329.  
  330.     BEGIN
  331.     {$IFC qDebug}
  332.     IF gClipWindow.fDocument = SELF THEN
  333.         ProgramBreak('Attempt to close clipboard document');
  334.     {$ENDC}
  335.  
  336.     changeCount := GetChangeCount;
  337.     IF changeCount <> 0 THEN
  338.         BEGIN
  339.         poseResult := PoseSaveDialog;
  340.         CASE poseResult OF
  341.             cancel:
  342.                 Failure(noErr, msgCancelled);
  343.         END;
  344.         END;
  345.  
  346.     lastCommand := GetLastCommand;
  347.     IF (lastCommand <> NIL) & (lastCommand.fChangedDocument = SELF) THEN
  348.         CommitLastCommand;
  349.  
  350.     IF changeCount <> 0 THEN
  351.         BEGIN
  352.         CASE poseResult OF
  353.             kYesButton:
  354.             {Will fail if unable to save}
  355.                 Save(cClose,                            {askForFilename:} NOT fSaveExists,
  356.                      kSwitchToTarget);
  357.             kNoButton:
  358.                 Abandon;
  359.             END;
  360.         END;
  361.  
  362.     ForAllWindowsDo(CloseAWindow);
  363.  
  364.     Free;
  365.     END;
  366.  
  367. {--------------------------------------------------------------------------------------------------}
  368. {$S MAClose}
  369.  
  370. PROCEDURE TDocument.DeleteView(viewToDelete: TView);
  371.  
  372.     BEGIN
  373.     IF (fViewList <> NIL) THEN
  374.         fViewList.Delete(viewToDelete);
  375.  
  376.     IF (fWindowList <> NIL) THEN
  377.         fWindowList.Delete(viewToDelete);                { Make sure the lists are in synch. ???
  378.                                                          should we only have one list now (post
  379.                                                          2.0) }
  380.     END;
  381.  
  382. {--------------------------------------------------------------------------------------------------}
  383. {$S MAClose}
  384.  
  385. PROCEDURE TDocument.DeleteWindow(windowToDelete: TWindow);
  386.  
  387.     BEGIN
  388.     IF (fWindowList <> NIL) THEN
  389.         fWindowList.Delete(windowToDelete);
  390.  
  391.     IF (fViewList <> NIL) THEN
  392.         fViewList.Delete(windowToDelete);                { Make sure the lists are in synch. ???
  393.                                                          should we only have one list now (post
  394.                                                          2.0) }
  395.     END;
  396.  
  397. {--------------------------------------------------------------------------------------------------}
  398. {$S MAFile}
  399.  
  400. FUNCTION TDocument.DiskFileChanged(checkType: BOOLEAN): OSErr;
  401.  
  402.     VAR
  403.         pb:                 HParamBlockRec;
  404.         err:                OSErr;
  405.         fi:                 FailInfo;
  406.  
  407.     PROCEDURE HdlGetFileInfo(error: OSErr;
  408.                              message: LONGINT);
  409.  
  410.         BEGIN
  411.         Free;
  412.         END;
  413.  
  414.     BEGIN
  415.     IF fSaveExists THEN
  416.         BEGIN
  417.         CatchFailures(fi, HdlGetFileInfo);
  418.         LockHandleHigh(Handle(fTitle));
  419.         {$Push} {$H-}
  420.         err := GetFileInfo(fTitle^^, fVolRefNum, pb);
  421.         {$Pop}
  422.         HUnLock(Handle(fTitle));
  423.         Success(fi);
  424.         IF (err = noErr) & checkType & (pb.ioFlFndrInfo.fdType <> fFileType) THEN
  425.             err := errFTypeChanged
  426.         ELSE IF pb.ioFlMdDat <> fModDate THEN
  427.             err := errFileChanged;
  428.         DiskFileChanged := err;
  429.         END
  430.     ELSE
  431.         DiskFileChanged := noErr;
  432.     END;
  433.  
  434. {--------------------------------------------------------------------------------------------------}
  435. {$S MAOpen}
  436.  
  437. PROCEDURE TDocument.DoInitialState;
  438. {Called for 'New' & 'Revert' [to blank] commands & for default open tool icon}
  439.  
  440.     BEGIN
  441.     END;
  442.  
  443. {--------------------------------------------------------------------------------------------------}
  444. {$S MAOpen}
  445.  
  446. PROCEDURE TDocument.DoMakeViews(forPrinting: BOOLEAN);
  447.  
  448. { E X A M P L E
  449.     VAR aYOURView: TYOURView;
  450. BEGIN
  451.     NEW(aYOURView);
  452.     aYOURView.IYOURView(SELF, YOURExtentRect);
  453.     DoMakeView := aYOURView;
  454. END;
  455. }
  456.  
  457.     VAR
  458.         aPrintHandler:        TPrintHandler;
  459.         aView:                TView;
  460.  
  461.     BEGIN
  462.     IF qTemplateViews THEN
  463.         BEGIN
  464.         IF forPrinting THEN                                 { Don't need window when Finder printing. }
  465.             aView := DoCreateViews(SELF, NIL, kDefaultViewID, gZeroVPt)
  466.         ELSE
  467.             aView := NewTemplateWindow(kDefaultWindowID, SELF);
  468.         FailNil(aView);
  469.     
  470.         { Install a copy of gPrintHandler into the view.  gPrintHandler will be a real printhandler
  471.         if UPrinting has been initialized otherwise it is a null print handler. }
  472.         aView := aView.FindSubView(kIDDefaultView);
  473.         aPrintHandler := TPrintHandler(gPrintHandler.clone);
  474.         fDocPrintHandler := aPrintHandler;
  475.         aPrintHandler.fDocument := SELF;
  476.         aPrintHandler.fView := aView;
  477.         aPrintHandler.SetDefaultPrintInfo;
  478.         aView.AttachPrintHandler(aPrintHandler);
  479.         END;
  480.     END;
  481.  
  482. {--------------------------------------------------------------------------------------------------}
  483. {$S MADocumentRes}
  484.  
  485. PROCEDURE TDocument.DoMakeWindows;
  486.  
  487.     BEGIN
  488.     END;
  489.  
  490. {--------------------------------------------------------------------------------------------------}
  491. {$S MASelCommand}
  492.  
  493. FUNCTION TDocument.DoMenuCommand(aCmdNumber: CmdNumber): TCommand;
  494.  
  495.     VAR
  496.         aSaveDocCommand:    TSaveDocCommand;
  497.         aRevertDocCommand:    TRevertDocCommand;
  498.         oldObjectPerm:        BOOLEAN;
  499.  
  500.     BEGIN
  501.     { ==================================================================================
  502.     Some commands will be returned to perform actions that must _ALWAYS_ be available.
  503.     The allocation cannot be allowed to fail.  So we do a temp allocation which by
  504.     definition cannot be allowed to fail.  This strategy is used wherever we want to use
  505.     command objects but don't want to leave the user twisting in the breeze.
  506.     NOTE: Don't forget to allow for this memory in your mem! resource if you copy this
  507.     style in your own code.
  508.     ================================================================================== }
  509.  
  510.     DoMenuCommand := NIL;
  511.  
  512.     CASE aCmdNumber OF
  513.  
  514.         cPrFileBase..cPrFileMax:
  515.             IF fDocPrintHandler <> NIL THEN
  516.                 DoMenuCommand := fDocPrintHandler.DoMenuCommand(aCmdNumber);
  517.  
  518.         cSave, cSaveAs, cSaveCopy:
  519.             BEGIN
  520.             oldObjectPerm := AllocateObjectsFromPerm(FALSE);
  521.             New(aSaveDocCommand);
  522.             IF AllocateObjectsFromPerm(oldObjectPerm) THEN;
  523.  
  524.             FailNil(aSaveDocCommand);                    { just in case }
  525.             aSaveDocCommand.ISaveDocCommand(aCmdNumber, SELF);
  526.             DoMenuCommand := aSaveDocCommand;
  527.             END;
  528.  
  529.         cRevert:
  530.             BEGIN
  531.             oldObjectPerm := AllocateObjectsFromPerm(FALSE);
  532.             New(aRevertDocCommand);
  533.             IF AllocateObjectsFromPerm(oldObjectPerm) THEN;
  534.  
  535.             FailNil(aRevertDocCommand);                    { just in case }
  536.             aRevertDocCommand.IRevertDocCommand(aCmdNumber, SELF);
  537.             DoMenuCommand := aRevertDocCommand;
  538.             END;
  539.  
  540.         OTHERWISE
  541.             DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  542.     END;                                                {CASE}
  543.     END;
  544.  
  545. {--------------------------------------------------------------------------------------------------}
  546. {$S MAWriteFile}
  547.  
  548. PROCEDURE TDocument.DoNeedDiskSpace(VAR dataForkBytes, rsrcForkBytes: LONGINT);
  549.  
  550.     BEGIN
  551.     IF fSavePrintInfo THEN
  552.         dataForkBytes := dataForkBytes + kPrintInfoSize;
  553.     IF fUsesRsrcFork THEN
  554.         rsrcForkBytes := rsrcForkBytes + kRsrcFileOverhead;
  555.     END;
  556.  
  557. {--------------------------------------------------------------------------------------------------}
  558. {$S MAReadFile}
  559.  
  560. PROCEDURE TDocument.DoRead(aRefNum: INTEGER;
  561.                            rsrcExists, forPrinting: BOOLEAN);
  562.  
  563.     VAR
  564.         count:                LONGINT;
  565.  
  566.     BEGIN
  567.     IF fSavePrintInfo THEN
  568.         BEGIN
  569.         IF fPrintInfo = NIL THEN
  570.             BEGIN
  571.             fPrintInfo := NewPermHandle(kPrintInfoSize);
  572.             FailNil(fPrintInfo);
  573.             END;
  574.  
  575.         count := kPrintInfoSize;
  576.         FailOSerr(FSRead(aRefNum, count, fPrintInfo^));
  577.         END;
  578.     END;
  579.  
  580. {--------------------------------------------------------------------------------------------------}
  581. {$S MADocumentRes}
  582.  
  583. PROCEDURE TDocument.DoSetupMenus;
  584.  
  585.     BEGIN
  586.     INHERITED DoSetupMenus;
  587.  
  588.     Enable(cSaveAs, TRUE);
  589.     Enable(cSaveCopy, TRUE);
  590.     IF GetChangeCount <> 0 THEN
  591.         BEGIN
  592.         Enable(cSave, TRUE);
  593.         Enable(cRevert, TRUE);
  594.         END;
  595.  
  596.     IF (fDocPrintHandler <> NIL) & (NOT gTarget.HandlesPrintingCommands) THEN
  597.         fDocPrintHandler.DoSetupMenus;
  598.     END;
  599.  
  600. {--------------------------------------------------------------------------------------------------}
  601. {$S MAWriteFile}
  602.  
  603. PROCEDURE TDocument.DoWrite(aRefNum: INTEGER;
  604.                             makingCopy: BOOLEAN);
  605.  
  606.     VAR
  607.         count:                LONGINT;
  608.  
  609.     BEGIN
  610.     UseResFile(fRsrcRefnum);                            {make sure it is the current map}
  611.     IF fSavePrintInfo THEN
  612.         BEGIN
  613.         IF fPrintInfo = NIL THEN
  614.             BEGIN
  615.             {$IFC qDebug}
  616.             ProgramBreak('no print record in document') {???}
  617.             {$ENDC}
  618.             END
  619.         ELSE
  620.             BEGIN
  621.             count := kPrintInfoSize;
  622.             FailOSerr(FSWrite(aRefNum, count, fPrintInfo^));
  623.             END;
  624.         END;
  625.     END;
  626.  
  627. {--------------------------------------------------------------------------------------------------}
  628. {$S MADocumentRes}
  629.  
  630. PROCEDURE TDocument.ForAllViewsDo(PROCEDURE DoToView(aView: TView));
  631.  
  632.     BEGIN
  633.     IF (fViewList <> NIL) THEN
  634.         fViewList.Each(DoToView);
  635.     END;
  636.  
  637. {--------------------------------------------------------------------------------------------------}
  638. {$S MADocumentRes}
  639.  
  640. PROCEDURE TDocument.ForAllWindowsDo(PROCEDURE DoToWind(aWindow: TWindow));
  641.  
  642.     BEGIN
  643.     IF (fWindowList <> NIL) THEN
  644.         fWindowList.Each(DoToWind);
  645.     END;
  646.  
  647. {--------------------------------------------------------------------------------------------------}
  648. {$S MADocumentRes}
  649.  
  650. PROCEDURE TDocument.FreeData;
  651.  
  652.     BEGIN
  653.     END;
  654.  
  655. {--------------------------------------------------------------------------------------------------}
  656. {$S MADocumentRes}
  657.  
  658. PROCEDURE TDocument.FreeFile;
  659.  
  660.     VAR
  661.         err:                OSErr;
  662.  
  663.     BEGIN
  664.     IF fDataOpen | fRsrcOpen THEN
  665.         BEGIN
  666.         err := CloseFile(fDataRefnum, fRsrcRefnum);
  667.         {$IFC qDebug}
  668.         IF err <> noErr THEN
  669.             Writeln('In TDocument.FreeFile: error from CloseFile = ', err: 1);
  670.         {$ENDC}
  671.         END;
  672.     END;
  673.  
  674. {--------------------------------------------------------------------------------------------------}
  675. {$S MAClipboard}
  676.  
  677. PROCEDURE TDocument.FreeFromClipboard;
  678.  
  679.     BEGIN
  680.     DeleteWindow(gClipWindow);
  681.  
  682.     Free;
  683.     END;
  684.  
  685. {--------------------------------------------------------------------------------------------------}
  686. {$S MADocumentRes}
  687.  
  688. FUNCTION TDocument.GetChangeCount: LONGINT;
  689.  
  690.     BEGIN
  691.     GetChangeCount := fChangeCount;
  692.     END;
  693.  
  694.  
  695. {--------------------------------------------------------------------------------------------------}
  696. {$S MAInspector}
  697.  
  698. PROCEDURE TDocument.GetInspectorName(VAR inspectorName: Str255); OVERRIDE;
  699.  
  700.     BEGIN
  701.     inspectorName := fTitle^^;
  702.     END;
  703.  
  704. {--------------------------------------------------------------------------------------------------}
  705. {$S MAWriteFile}
  706.  
  707. FUNCTION TDocument.GetSaveInfo(itsCmdNumber: CmdNumber;
  708.                                copyFInfo: BOOLEAN;
  709.                                VAR cInfo: CInfoPBRec): BOOLEAN;
  710.  
  711.     VAR
  712.         currName:            Str255;
  713.         err:                OSErr;
  714.  
  715.     BEGIN
  716.     IF fSaveExists & copyFInfo THEN
  717.         BEGIN
  718.         currName := fTitle^^;
  719.         WITH cInfo DO
  720.             BEGIN
  721.             ioNamePtr := @currName;
  722.             ioVRefnum := fVolRefNum;
  723.             ioFVersNum := 0;
  724.             ioFDirIndex := 0;
  725.             ioDirID := 0;
  726.             END;
  727.         IF qNeedsROM128K | gConfiguration.hasHFS THEN
  728.             BEGIN
  729.             err := FillInDirID(@cInfo);
  730.             IF err = noErr THEN
  731.                 err := PBGetCatInfo(@cInfo, FALSE);
  732.             END
  733.         ELSE
  734.             err := PBGetFInfo(@cInfo, FALSE);
  735.  
  736.         cInfo.ioNamePtr := NIL;                         {since ptr is invalid when we exit}
  737.  
  738.             {set the type and creator in case it has changed;
  739.                 the file might be on a file server and someone else
  740.                 could have changed the document}
  741.         cInfo.ioFlFndrInfo.fdCreator := fCreator;
  742.         cInfo.ioFlFndrInfo.fdType := fFileType;
  743.         END
  744.     ELSE
  745.         err := fnfErr;                                    {fake error}
  746.  
  747.     IF err = noErr THEN
  748.         GetSaveInfo := TRUE
  749.     ELSE
  750.         BEGIN
  751.         WITH cInfo.ioFlFndrInfo DO
  752.             BEGIN
  753.             fdCreator := fCreator;
  754.             fdType := fFileType;
  755.             END;
  756.  
  757.         GetSaveInfo := FALSE
  758.         END;
  759.     END;
  760.  
  761. {--------------------------------------------------------------------------------------------------}
  762. {$S MAFile}
  763.  
  764. PROCEDURE TDocument.GetTempName(VAR filename: Str255);
  765.  
  766.     CONST
  767.         maxName             = 31;                        {maximum name size to generate}
  768.         maxNumber            = 10;                        {maximum # digits of the random number}
  769.         maxPrefix            = maxName - maxNumber;
  770.  
  771.     VAR
  772.         s:                    Str255;
  773.         apRefnum:            INTEGER;
  774.         apParam:            Handle;
  775.         time:                LONGINT;
  776.  
  777.     BEGIN
  778.     {If the document is untitled, use the application name.}
  779.     IF fTitle^^ = '' THEN
  780.         GetAppParms(filename, apRefnum, apParam)
  781.     ELSE
  782.         filename := fTitle^^;
  783.  
  784.     IF Length(filename) > maxPrefix THEN
  785.         filename := Copy(filename, 1, maxPrefix);
  786.  
  787.     {append a pseudo-random number}
  788.     GetDateTime(time);
  789.     NumToString(ABS(BXOR(time, BRotR(TickCount, 16))), s);
  790.     filename := CONCAT(filename, s);
  791.     END;
  792.  
  793. {--------------------------------------------------------------------------------------------------}
  794. {$S MADocumentRes}
  795.  
  796. FUNCTION TDocument.HandlesPrintingCommands: BOOLEAN; OVERRIDE;
  797.  
  798.     BEGIN
  799.     HandlesPrintingCommands := FALSE;
  800.     END;
  801.  
  802. {--------------------------------------------------------------------------------------------------}
  803. {$S MAWriteFile}
  804.  
  805. PROCEDURE TDocument.MakeNewCopy(makingCopy: BOOLEAN;
  806.                                 validFInfo: BOOLEAN;
  807.                                 VAR cInfo: CInfoPBRec);
  808.  
  809.      {This routine changes the following fields of cInfo:
  810.  
  811.       The ioNamePtr, and ioVRefnum fields must be set to indicate
  812.         the desired file; this routine sets ioDirID to 0.
  813.  
  814.      IF setFInfo is TRUE then all the fields of cInfo
  815.         should be set up.  Otherwise only the file type and creator
  816.         need to be set up.}
  817.  
  818.     VAR
  819.         err:                OSErr;
  820.         dataRefnum:         INTEGER;
  821.         rsrcRefnum:         INTEGER;
  822.         oldVRefnum:         INTEGER;
  823.         fi:                 FailInfo;
  824.  
  825.     PROCEDURE HdlMkNewCopy(error: OSErr;
  826.                            message: LONGINT);
  827.  
  828.         VAR
  829.             err:                OSErr;
  830.  
  831.         BEGIN
  832.         err := CloseFile(dataRefnum, rsrcRefnum);
  833.         {$IFC qDebug}
  834.         IF err <> noErr THEN
  835.             Writeln('In HdlMkNewCopy: error from CloseFile is ', err: 1);
  836.         {$ENDC}
  837.  
  838.         err := DeleteFile(cInfo.ioNamePtr, cInfo.ioVRefnum);
  839.         {$IFC qDebug}
  840.         IF (err <> noErr) & (err <> fnfErr) THEN
  841.             Writeln('In HdlMkNewCopy: error from DeleteFile is ', err: 1);
  842.         {$ENDC}
  843.         END;
  844.  
  845.     BEGIN
  846.     IF fUsesDataFork | fUsesRsrcFork THEN
  847.         BEGIN
  848.         cInfo.ioDirID := 0;
  849.  
  850.         {Initalize these in case we fail before the call to OpenAFile.}
  851.         dataRefnum := kNoFileRefnum;
  852.         rsrcRefnum := kNoFileRefnum;
  853.  
  854.             {Create the file with the desired creator/type,
  855.                 in case we are not going to set the file info
  856.                 below.}
  857.         FailOSerr(Create(cInfo.ioNamePtr^, cInfo.ioVRefnum, cInfo.ioFlFndrInfo.fdCreator,
  858.                          cInfo.ioFlFndrInfo.fdType));
  859.  
  860.         CatchFailures(fi, HdlMkNewCopy);
  861.  
  862.         IF fUsesRsrcFork THEN
  863.             BEGIN
  864.             FailOSerr(GetVol(NIL, oldVRefnum));
  865.             FailOSerr(SetVol(NIL, cInfo.ioVRefnum));
  866.  
  867.             CreateResFile(cInfo.ioNamePtr^);
  868.  
  869.                 {Do this here to ensure that the current volume is
  870.                     reset, even if we fail.}
  871.             FailOSerr(SetVol(NIL, oldVRefnum));
  872.  
  873.             FailResError;                                {this checks the call to CreateResFile}
  874.             END;
  875.  
  876.         IF validFInfo & (NOT makingCopy) THEN
  877.             BEGIN
  878.             IF qNeedsROM128K | gConfiguration.hasHFS THEN
  879.                 FailOSerr(PBSetCatInfo(@cInfo, FALSE))    {??? Poor Man's Search Path ???}
  880.             ELSE
  881.                 BEGIN
  882.                         {NOTE: We can use the cInfo for this call since the
  883.                             fields required by PBSetInfo are a subset of
  884.                             those in the CInfoPBRec; except we set the
  885.                             ioFVersNum field (just in case)}
  886.                 cInfo.ioFVersNum := 0;
  887.                 FailOSerr(PBSetFInfo(@cInfo, FALSE));
  888.                 END;
  889.             END;
  890.  
  891.         FailOSerr(OpenAFile(cInfo.ioNamePtr^, cInfo.ioVRefnum, fUsesDataFork, fUsesRsrcFork,
  892.                             fsRdWrPerm, fsRdWrPerm, dataRefnum, rsrcRefnum));
  893.  
  894.         DoWrite(dataRefnum, makingCopy);
  895.  
  896.         Success(fi);
  897.  
  898.         FailOSerr(CloseFile(dataRefnum, rsrcRefnum));
  899.         END;
  900.     END;
  901.  
  902. {--------------------------------------------------------------------------------------------------}
  903. {$S MAFile}
  904.  
  905. FUNCTION TDocument.OpenAFile(name: Str255;
  906.                              volRefnum: INTEGER;
  907.                              openData, openRsrc: BOOLEAN;
  908.                              dataPerm, rsrcPerm: INTEGER;
  909.                              VAR dataRefnum, rsrcRefnum: INTEGER): OSErr;
  910.  
  911.     BEGIN
  912.     OpenAFile := MAOpenFile(name, volRefnum, openData, openRsrc, dataPerm, rsrcPerm, dataRefnum,
  913.                             rsrcRefnum);
  914.     END;
  915.  
  916. {--------------------------------------------------------------------------------------------------}
  917. {$S MAOpen}
  918.  
  919. PROCEDURE TDocument.OpenAgain(itsCmdNumber: CmdNumber;
  920.                               openingDoc: TDocument);
  921.  
  922.     VAR
  923.         window:             TWindow;
  924.         s:                    Str255;
  925.  
  926.     BEGIN
  927.     s := fTitle^^;                                        { because ParamText allocates memory }
  928.     ParamText(s, '', '', '');
  929.  
  930.     IF fReopenAlert THEN
  931.         StdAlert(phReopenDoc);                            {!!! This should be programatically
  932.                                                          defeatable }
  933.  
  934.     IF (fWindowList <> NIL) THEN
  935.         BEGIN
  936.         window := TWindow(fWindowList.First);            {??? this seems funky }
  937.         window.Select;
  938.         END;
  939.  
  940.     Failure(0, 0);
  941.     END;
  942.  
  943. {--------------------------------------------------------------------------------------------------}
  944. {$S MAClose}
  945.  
  946. FUNCTION TDocument.PoseSaveDialog: INTEGER;
  947.  
  948.     VAR
  949.         idx:                INTEGER;
  950.         name:                Str255;
  951.         reason:             Str255;
  952.  
  953.     BEGIN
  954.     IF GetChangeCount <> 0 THEN
  955.         BEGIN
  956.         IF gAppDone THEN
  957.             idx := bzQuitting
  958.         ELSE
  959.             idx := bzClosing;
  960.  
  961.         GetIndString(reason, kIDBuzzString, idx);
  962.         name := fTitle^^;                                { ParamText can compact heap }
  963.         ParamText(name, reason, '', '');
  964.  
  965.         PoseSaveDialog := MacAppAlert(phSaveChanges, NIL); {!!! This should be programatically
  966.                                                             defeatable }
  967.         END
  968.     ELSE
  969.         PoseSaveDialog := kNoButton;
  970.     END;
  971.  
  972. {--------------------------------------------------------------------------------------------------}
  973. {$S MAReadFile}
  974.  
  975. PROCEDURE TDocument.ReadFromFile(VAR anAppFile: AppFile;
  976.                                  forPrinting: BOOLEAN);
  977.  
  978.     VAR
  979.         fi:                 FailInfo;
  980.         dataRefnum:         INTEGER;
  981.         rsrcRefnum:         INTEGER;
  982.         isRevert:            BOOLEAN;
  983.         shouldOpenData:     BOOLEAN;
  984.         shouldOpenRsrc:     BOOLEAN;
  985.  
  986.     PROCEDURE HdlRead(error: INTEGER;
  987.                       message: LONGINT);
  988.  
  989.         VAR
  990.             err:                INTEGER;
  991.  
  992.         BEGIN
  993.         err := CloseFile(dataRefnum, rsrcRefnum);
  994.         {$IFC qDebug}
  995.         IF err <> noErr THEN
  996.             Writeln('In HdlOpen: error from CloseFile is ', err: 1);
  997.         {$ENDC}
  998.  
  999.         {caller should set the message as appropriate}
  1000.         END;
  1001.  
  1002.     BEGIN
  1003.     isRevert := (anAppFile.fName = '');                 {signal to re-read file}
  1004.     IF isRevert THEN
  1005.         BEGIN
  1006.         anAppFile.fName := fTitle^^;
  1007.         anAppFile.vRefnum := fVolRefNum;
  1008.         END
  1009.     ELSE
  1010.         BEGIN
  1011.         SetString(fTitle, anAppFile.fName);
  1012.         IF fTitle^^ <> anAppFile.fName THEN             { ??? how to test if SetString worked ??? }
  1013.             FailOSerr(memFullErr);
  1014.         fVolRefNum := anAppFile.vRefnum;
  1015.         END;
  1016.  
  1017.     {Don't attempt to open the fork(s) again if they're already open.
  1018.         We do all this rather than close and reopen so that we need not
  1019.         search the directory again, an expensive operation on large MFS
  1020.         disks.}
  1021.     shouldOpenData := fUsesDataFork & NOT (fDataOpen & isRevert);
  1022.     shouldOpenRsrc := fUsesRsrcFork & NOT (fRsrcOpen & isRevert);
  1023.  
  1024.     {Make sure CloseFile operates properly if OpenAFile fails.}
  1025.     dataRefnum := kNoFileRefnum;
  1026.     rsrcRefnum := kNoFileRefnum;
  1027.  
  1028.     CatchFailures(fi, HdlRead);
  1029.  
  1030.     FailOSerr(OpenAFile(anAppFile.fName, anAppFile.vRefnum, shouldOpenData, shouldOpenRsrc,
  1031.                         fDataPerm, fRsrcPerm, dataRefnum, rsrcRefnum));
  1032.  
  1033.     fSaveExists := TRUE;
  1034.  
  1035.     {If the file is already open, use the refnums we already have.
  1036.         Make sure that the document's data fork is positioned at TOF.
  1037.         Make sure that the document's resource file is on top.}
  1038.     IF fDataOpen & NOT shouldOpenData THEN
  1039.         BEGIN
  1040.         dataRefnum := fDataRefnum;
  1041.         FailOSerr(SetFPos(dataRefnum, fsFromStart, 0));
  1042.         END;
  1043.     IF fRsrcOpen & NOT shouldOpenRsrc THEN
  1044.         BEGIN
  1045.         rsrcRefnum := fRsrcRefnum;
  1046.         UseResFile(rsrcRefnum);
  1047.         END;
  1048.  
  1049.     DoRead(dataRefnum,                                    {rsrcExists:} rsrcRefnum <> kNoFileRefnum,
  1050.            forPrinting);
  1051.  
  1052.     SetChangeCount(0);
  1053.  
  1054.     Success(fi);
  1055.  
  1056.     IF fDataOpen THEN
  1057.         BEGIN
  1058.         fDataRefnum := dataRefnum;                        {save valid refnum}
  1059.         dataRefnum := kNoFileRefnum;                    {make sure CloseFile doesn't close it}
  1060.         END
  1061.     ELSE
  1062.         fDataRefnum := kNoFileRefnum;                    {save the no file refnum}
  1063.  
  1064.     IF fRsrcOpen THEN
  1065.         BEGIN
  1066.         fRsrcRefnum := rsrcRefnum;                        {save valid refnum}
  1067.         UseResFile(fRsrcRefnum);                        {make sure it is the current map}
  1068.         rsrcRefnum := kNoFileRefnum;                    {make sure CloseFile doesn't close it}
  1069.         END
  1070.     ELSE
  1071.         fRsrcRefnum := kNoFileRefnum;                    {save the no file refnum}
  1072.  
  1073.     FailOSerr(CloseFile(dataRefnum, rsrcRefnum));
  1074.  
  1075.     fModDate := FileModDate(anAppFile.fName, anAppFile.vRefnum);
  1076.     END;
  1077.  
  1078. {--------------------------------------------------------------------------------------------------}
  1079. {$S MAWriteFile}
  1080.  
  1081. PROCEDURE TDocument.RequestFileName(itsCmdNumber: CmdNumber;
  1082.                                     makingCopy: BOOLEAN;
  1083.                                     VAR filename: Str255;
  1084.                                     VAR volRefnum: INTEGER);
  1085.  
  1086.     VAR
  1087.         reply:                SFReply;
  1088.         dlgID:                INTEGER;
  1089.         prompt:             Str255;
  1090.         dlgLoc:             Point;
  1091.         dlgHook:            ProcPtr;
  1092.         filterProc:         ProcPtr;
  1093.         otherDoc:            TDocument;
  1094.         err:                OSErr;
  1095.  
  1096.     BEGIN
  1097.     filename := fTitle^^;
  1098.     SFPutParms(itsCmdNumber, dlgID, dlgLoc, filename, prompt, dlgHook, filterProc);
  1099.  
  1100.     {$IFC qDebug}
  1101.     gRsrcCheck := 0;                                    {force immediate check}
  1102.     {$ENDC}
  1103.  
  1104.     {Update all the windows to avoid a bug in Standard File in which
  1105.         you can't mount a disk correctly when window updates are pending.}
  1106.     gApplication.UpdateAllWindows;
  1107.  
  1108.     SFPPutFile(dlgLoc, prompt, filename, dlgHook, reply, dlgID, filterProc);
  1109.  
  1110.     IF reply.good THEN
  1111.         BEGIN
  1112.         filename := reply.fName;
  1113.         volRefnum := reply.vRefnum;
  1114.  
  1115.         {See if there is an open document with the same name.  If there
  1116.             is, tell it we're trying to save it again, which will
  1117.             ordinarily result in failure.}
  1118.         otherDoc := gApplication.AlreadyOpen(filename, volRefnum);
  1119.         IF otherDoc <> NIL THEN
  1120.             otherDoc.SaveAgain(itsCmdNumber, makingCopy, SELF);
  1121.  
  1122.         {User has already confirmed deleting target in this case,
  1123.             so trash file and get maximum disk space.}
  1124.         err := DeleteFile(@filename, volRefnum);
  1125.         IF (err <> noErr) & (err <> fnfErr) THEN
  1126.             Failure(err, 0);
  1127.         END
  1128.     ELSE
  1129.         Failure(noErr, msgCancelled);                    {user cancelled}
  1130.     END;
  1131.  
  1132. {--------------------------------------------------------------------------------------------------}
  1133. {$S MAReadFile}
  1134.  
  1135. PROCEDURE TDocument.Revert;
  1136.  
  1137.     VAR
  1138.         anAppFile:            AppFile;
  1139.         fi:                 FailInfo;
  1140.         lastCommand:        TCommand;
  1141.  
  1142.     PROCEDURE HdlRevert(error: OSErr;
  1143.                         message: LONGINT);
  1144.  
  1145.         BEGIN
  1146.         IF error = fnfErr THEN
  1147.             error := errRevertFNF;
  1148.         IF message = 0 THEN
  1149.             gErrorParm3 := fTitle^^;
  1150.         FailNewMessage(error, message, msgRevertFailed);
  1151.         END;
  1152.  
  1153.     PROCEDURE ResetPrintHandler(view: TView);
  1154.  
  1155.         BEGIN
  1156.         IF view.fPrintHandler <> NIL THEN
  1157.             view.fPrintHandler.Reset;
  1158.         END;
  1159.  
  1160.     BEGIN
  1161.     CatchFailures(fi, HdlRevert);
  1162.  
  1163.     CheckDiskFile(kIDBuzzString, bzRevertAnyways,        {reverting:} TRUE);
  1164.  
  1165.     lastCommand := GetLastCommand;
  1166.     IF (lastCommand <> NIL) & (lastCommand.fChangedDocument = SELF) THEN
  1167.         CommitLastCommand;
  1168.  
  1169.     FreeData;
  1170.  
  1171.     IF fSaveExists THEN
  1172.         BEGIN
  1173.         anAppFile.fName := '';                            {signal that we are reverting}
  1174.         ReadFromFile(anAppFile, kForDisplay);
  1175.         END
  1176.     ELSE
  1177.         BEGIN
  1178.         IF (fViewList <> NIL) THEN
  1179.             fViewList.Each(ResetPrintHandler);
  1180.         DoInitialState;
  1181.         END;
  1182.  
  1183.     SetChangeCount(0);
  1184.  
  1185.     Success(fi);
  1186.     END;
  1187.  
  1188.     {Notes:
  1189.         Parameter combinations:
  1190.             ask?    copy?
  1191.              T          F     Save As... or Save of Untitled doc
  1192.              T          T     Save a Copy In...
  1193.              F          F     Save of a titled doc
  1194.  
  1195.         Caller is responsible for passing askForFilename = TRUE
  1196.             if the document is currently untitled.
  1197.  
  1198.         If askForFilename is FALSE, we force switchToTarget to TRUE.
  1199.  
  1200.         We call SaveInPlace only if there is insufficient disk space
  1201.             to save a temporary copy and we are allowed to save in
  1202.             place (according to the fSaveInPlace field).
  1203.  
  1204.         If askForFilename is TRUE, SaveInPlace should not be called
  1205.             since we delete the target immediately.  (If there is not
  1206.             enough room to save via a temporary file, we would not
  1207.             be able to save in place either.)
  1208.     }
  1209.  
  1210. {--------------------------------------------------------------------------------------------------}
  1211. {$S MAReadFile}
  1212.  
  1213. PROCEDURE TDocument.Abandon;
  1214.  
  1215.     BEGIN
  1216.     { If your document needs to do some cleanup when its being abandoned then
  1217.     put some code in an override of this method }
  1218.     END;
  1219.  
  1220. {--------------------------------------------------------------------------------------------------}
  1221. {$S MAWriteFile}
  1222.  
  1223. PROCEDURE TDocument.Save(itsCmdNumber: CmdNumber;
  1224.                          askForFilename, makingCopy: BOOLEAN);
  1225.  
  1226.     VAR
  1227.         name:                Str255;
  1228.         volRefnum:            INTEGER;
  1229.  
  1230.         hPB:                HParamBlockRec;
  1231.  
  1232.         dataBytes:            LONGINT;
  1233.         rsrcBytes:            LONGINT;
  1234.         neededBlks:         LONGINT;
  1235.         usedBlks:            LONGINT;
  1236.         freeBlks:            LONGINT;
  1237.         blkSize:            LONGINT;
  1238.  
  1239.         copyFInfo:            BOOLEAN;
  1240.         canSaveInPlace:     BOOLEAN;
  1241.         oldFlag:            BOOLEAN;
  1242.         fi:                 FailInfo;
  1243.         err:                OSErr;
  1244.  
  1245.         otherDoc:            TDocument;
  1246.         lastCommand:        TCommand;
  1247.  
  1248.     PROCEDURE HdlSave(error: INTEGER;
  1249.                       message: LONGINT);
  1250.  
  1251.         VAR
  1252.             newMsg:             LONGINT;
  1253.  
  1254.         BEGIN
  1255.         err := FlushVol(NIL, volRefnum);
  1256.  
  1257.         IF message = 0 THEN
  1258.             gErrorParm3 := name;
  1259.  
  1260.         IF NOT askForFilename THEN
  1261.             newMsg := msgSaveFailed
  1262.         ELSE IF makingCopy THEN
  1263.             newMsg := msgSaveCopyFailed
  1264.         ELSE
  1265.             newMsg := msgSaveAsFailed;
  1266.  
  1267.         FailNewMessage(error, message, newMsg);
  1268.         END;
  1269.  
  1270.     BEGIN
  1271.     CatchFailures(fi, HdlSave);
  1272.  
  1273.     {Step 1: Get the target of the save}
  1274.     {Caller should set askForFilename if this is an Untitled document}
  1275.     IF askForFilename THEN
  1276.         RequestFileName(itsCmdNumber, makingCopy, name, volRefnum)
  1277.     ELSE
  1278.         BEGIN
  1279.         name := fTitle^^;
  1280.         volRefnum := fVolRefNum;
  1281.         END;
  1282.  
  1283. {Step 2: Decide whether to save with a temporary file or in place,
  1284.         and call appropriate method (SaveViaTemp or SaveInPlace).}
  1285.  
  1286.     copyFInfo := NOT (askForFilename | makingCopy);
  1287.  
  1288.     IF copyFInfo THEN
  1289.         CheckDiskFile(kIDBuzzString, bzSaveAnyways,     {reverting:} FALSE);
  1290.  
  1291.     AboutToSave(itsCmdNumber, name, volRefnum, makingCopy);
  1292.  
  1293.     lastCommand := GetLastCommand;
  1294.     IF fCommitOnSave | (NOT makingCopy) & (lastCommand <> NIL) & (lastCommand.fChangedDocument =
  1295.        SELF) THEN
  1296.         CommitLastCommand;
  1297.  
  1298.     {Get information about the volume saving to}
  1299.     WITH hPB DO
  1300.         BEGIN
  1301.         ioNamePtr := NIL;
  1302.         ioVRefnum := volRefnum;
  1303.         ioVolIndex := 0;
  1304.         END;
  1305.     FailOSerr(PBHGetVInfo(@hPB, FALSE));
  1306.  
  1307.         {on HFS ioVFrBlk is an unsigned INTEGER; on MFS it is
  1308.             limited to a positive signed INTEGER}
  1309.     freeBlks := BAND(hPB.ioVFrBlk, $0000FFFF) - 1;        {-1 for some slop -- don't try to fill up
  1310.                                                          the disk completely}
  1311.  
  1312.     {compute size needed to save document}
  1313.     blkSize := hPB.ioVAlBlkSiz;
  1314.  
  1315.     dataBytes := 0;
  1316.     rsrcBytes := 0;
  1317.     DoNeedDiskSpace(dataBytes, rsrcBytes);
  1318.     neededBlks := NumBlocks(rsrcBytes, blkSize) + NumBlocks(dataBytes, blkSize);
  1319.  
  1320.     IF freeBlks >= neededBlks THEN
  1321.     {enough disk space to create a second copy of document}
  1322.         SaveViaTemp(itsCmdNumber, makingCopy, copyFInfo, name, volRefnum)
  1323.     ELSE
  1324.         BEGIN                                            {cannot make a duplicate of document}
  1325.         {Check to see if we can save the file in place.}
  1326.  
  1327.         canSaveInPlace := FALSE;                        {default value}
  1328.  
  1329.         IF fSaveInPlace <> sipNever THEN
  1330.             BEGIN
  1331.             {See if target exists, if the disk space it uses is
  1332.                 enough to allow us to save the file after deleting it.}
  1333.             err := GetFileInfo(name, volRefnum, hPB);
  1334.  
  1335.             IF err = noErr THEN
  1336.                 BEGIN
  1337.                 {compute # block used by target}
  1338.                 usedBlks := NumBlocks(hPB.ioFlRPyLen, blkSize) + NumBlocks(hPB.ioFlPyLen, blkSize);
  1339.  
  1340.                 IF neededBlks <= usedBlks + freeBlks THEN
  1341.                 {we could save if target is deleted first}
  1342.                     BEGIN
  1343.                     IF fSaveInPlace = sipAskUser THEN
  1344.                         BEGIN
  1345.                         ParamText(name, '', '', '');
  1346.                         IF MacAppAlert(phPurgeOld, NIL) = kYesButton THEN {!!! This should be
  1347.                                                                            programatically
  1348.                                                                            defeatable }
  1349.                             canSaveInPlace := TRUE
  1350.                         ELSE
  1351.                             Failure(noErr, msgCancelled);
  1352.                         END
  1353.                     ELSE                                {we know fSaveInPlace <> sipNever; it must
  1354.                                                          be sipAlways}
  1355.                         canSaveInPlace := TRUE;
  1356.                     END;
  1357.                 END
  1358.             ELSE IF err <> fnfErr THEN
  1359.                 Failure(err, 0);
  1360.             END;
  1361.  
  1362.         IF canSaveInPlace THEN
  1363.             SaveInPlace(itsCmdNumber, makingCopy, copyFInfo, name, volRefnum)
  1364.         ELSE
  1365.             Failure(dskFulErr, 0);
  1366.         END;
  1367.  
  1368.     Success(fi);                                        {??? Put later in proc ???}
  1369.  
  1370.     {$IFC qDebug}
  1371.     err := GetFileInfo(name, volRefnum, hPB);
  1372.     IF err = noErr THEN
  1373.         BEGIN
  1374.         usedBlks := NumBlocks(hPB.ioFlRPyLen, blkSize) + NumBlocks(hPB.ioFlPyLen, blkSize);
  1375.         IF usedBlks <> neededBlks THEN
  1376.             BEGIN
  1377.             Writeln('In TDocument.Save: DoNeedDiskSpace estimated disk space incorrectly.');
  1378.             Writeln('estimated # disk blocks = ', neededBlks: 1);
  1379.             Writeln('   actual # disk blocks = ', usedBlks: 1);
  1380.             END;
  1381.         END;
  1382.     {$ENDC}
  1383.  
  1384.     {Step 3: Tell the document that the save was successful.}
  1385.     IF NOT makingCopy THEN
  1386.         SavedOn(name, volRefnum);
  1387.  
  1388.     err := FlushVol(NIL, volRefnum);
  1389.     END;
  1390.  
  1391. {--------------------------------------------------------------------------------------------------}
  1392. {$S MAWriteFile}
  1393.  
  1394. PROCEDURE TDocument.SaveAgain(itsCmdNumber: CmdNumber;
  1395.                               makingCopy: BOOLEAN;
  1396.                               savingDoc: TDocument);
  1397.  
  1398.     BEGIN
  1399.     {Don't save the file if another one of the same name is already open.}
  1400.     IF savingDoc <> SELF THEN
  1401.         Failure(errSaveAgain, 0);
  1402.     END;
  1403.  
  1404. {--------------------------------------------------------------------------------------------------}
  1405. {$S MAWriteFile}
  1406.  
  1407. PROCEDURE TDocument.SavedOn(VAR filename: Str255;
  1408.                             volRefnum: INTEGER);
  1409.  
  1410.     VAR
  1411.         dataRefnum:         INTEGER;
  1412.         rsrcRefnum:         INTEGER;
  1413.  
  1414.     BEGIN
  1415.     SetChangeCount(0);
  1416.     fSaveExists := TRUE;
  1417.  
  1418.     IF fTitle^^ <> filename THEN
  1419.         SetTitle(filename);
  1420.     fVolRefNum := volRefnum;
  1421.  
  1422.     fModDate := FileModDate(filename, volRefnum);
  1423.  
  1424.     FailOSerr(OpenAFile(filename, volRefnum, fDataOpen, fRsrcOpen, fDataPerm, fRsrcPerm, dataRefnum,
  1425.                         rsrcRefnum));
  1426.     fDataRefnum := dataRefnum;
  1427.     fRsrcRefnum := rsrcRefnum;
  1428.     END;
  1429.  
  1430. {--------------------------------------------------------------------------------------------------}
  1431. {$S MAWriteFile}
  1432.  
  1433. PROCEDURE TDocument.SaveInPlace(itsCmdNumber: CmdNumber;
  1434.                                 makingCopy, copyFInfo: BOOLEAN;
  1435.                                 VAR filename: Str255;
  1436.                                 volRefnum: INTEGER);
  1437. {fileName is VAR only to avoid copying}
  1438.  
  1439.     VAR
  1440.         cInfo:                CInfoPBRec;
  1441.         validInfo:            BOOLEAN;
  1442.         err:                OSErr;
  1443.  
  1444.     BEGIN
  1445.     IF NOT (fDataOpen | fRsrcOpen) THEN
  1446.         BEGIN
  1447.         validInfo := GetSaveInfo(itsCmdNumber, copyFInfo, cInfo);
  1448.  
  1449.         {Tell document that the file is going away.}
  1450.         FreeFile;
  1451.  
  1452.         {Delete the current file.}
  1453.         err := DeleteFile(@filename, volRefnum);
  1454.         IF (err <> noErr) & (err <> fnfErr) THEN
  1455.             Failure(err, 0);
  1456.  
  1457.         {Save a new copy.}
  1458.         cInfo.ioNamePtr := @filename;
  1459.         cInfo.ioVRefnum := volRefnum;
  1460.  
  1461.         MakeNewCopy(makingCopy, validInfo, cInfo);
  1462.         END
  1463.     ELSE
  1464.         BEGIN
  1465.         {$IFC qDebug}
  1466.         ProgramBreak('You must override TDocument.SaveInPlace for a disk-based document.');
  1467.         {$ENDC}
  1468.         END;
  1469.     END;
  1470.  
  1471. {--------------------------------------------------------------------------------------------------}
  1472. {$S MAWriteFile}
  1473.  
  1474. PROCEDURE TDocument.SaveViaTemp(itsCmdNumber: CmdNumber;
  1475.                                 makingCopy, copyFInfo: BOOLEAN;
  1476.                                 VAR filename: Str255;
  1477.                                 volRefnum: INTEGER);
  1478. {fileName is VAR only to avoid copying}
  1479.  
  1480.     VAR
  1481.         cInfo:                CInfoPBRec;
  1482.         validInfo:            BOOLEAN;
  1483.         tmpName:            Str255;
  1484.         fi:                 FailInfo;
  1485.         err:                OSErr;
  1486.  
  1487.     PROCEDURE HdlSvTemp(error: OSErr;
  1488.                         message: LONGINT);
  1489.  
  1490.         VAR
  1491.             err:                OSErr;
  1492.  
  1493.         BEGIN
  1494.         err := DeleteFile(@tmpName, volRefnum);
  1495.         {$IFC qDebug}
  1496.         IF (err <> noErr) & (err <> fnfErr) THEN
  1497.             Writeln('In HdlSvTemp: error from DeleteFile is ', err: 1);
  1498.         {$ENDC}
  1499.         END;
  1500.  
  1501.     PROCEDURE HdlSaveFailed(error: OSErr;
  1502.                             message: LONGINT);
  1503.  
  1504.         VAR
  1505.             dataRefnum:         INTEGER;
  1506.             rsrcRefnum:         INTEGER;
  1507.             fi:                 FailInfo;
  1508.  
  1509.             {If reopen attempt fails, make sure original error gets through.}
  1510.  
  1511.         PROCEDURE HdlFailFailed(newError: OSErr;
  1512.                                 newMessage: LONGINT);
  1513.  
  1514.             BEGIN
  1515.             Failure(error, message);
  1516.             END;
  1517.  
  1518.         BEGIN
  1519.         HdlSvTemp(error, message);
  1520.         IF fSaveExists & (NOT makingCopy) THEN
  1521.             BEGIN
  1522.             CatchFailures(fi, HdlFailFailed);
  1523.             LockHandleHigh(Handle(fTitle));
  1524.             FailOSerr(OpenAFile(fTitle^^, fVolRefNum, fDataOpen, fRsrcOpen, fDataPerm, fRsrcPerm,
  1525.                                 dataRefnum, rsrcRefnum));
  1526.             HUnLock(Handle(fTitle));
  1527.             Success(fi);
  1528.             fDataRefnum := dataRefnum;
  1529.             fRsrcRefnum := rsrcRefnum;
  1530.             END;
  1531.         END;
  1532.  
  1533.     BEGIN
  1534.     validInfo := GetSaveInfo(itsCmdNumber, copyFInfo, cInfo);
  1535.  
  1536.     GetTempName(tmpName);
  1537.  
  1538.     cInfo.ioNamePtr := @tmpName;
  1539.     cInfo.ioVRefnum := volRefnum;
  1540.  
  1541.     MakeNewCopy(makingCopy, validInfo, cInfo);
  1542.  
  1543.     CatchFailures(fi, HdlSvTemp);
  1544.  
  1545.     {Tell document that the file is going away}
  1546.     IF NOT makingCopy THEN
  1547.         FreeFile;
  1548.  
  1549.     Success(fi);
  1550.  
  1551.     CatchFailures(fi, HdlSaveFailed);
  1552.  
  1553.     {Delete the old copy if it exists.}
  1554.     err := DeleteFile(@filename, volRefnum);
  1555.     IF (err <> noErr) & (err <> fnfErr) THEN
  1556.         Failure(err, 0);
  1557.  
  1558.     FailOSerr(Rename(tmpName, volRefnum, filename));
  1559.  
  1560.     Success(fi);
  1561.     END;
  1562.  
  1563. {--------------------------------------------------------------------------------------------------}
  1564. {$S MADocumentRes}
  1565.  
  1566. PROCEDURE TDocument.SetTitle(aTitle: Str255);
  1567.  
  1568.     PROCEDURE InstallTitle(aWindow: TWindow);
  1569.  
  1570.         BEGIN
  1571.         aWindow.SetTitleForDoc(aTitle);
  1572.         END;
  1573.  
  1574.     BEGIN
  1575.     SetString(fTitle, aTitle);
  1576.     IF fTitle^^ <> aTitle THEN                            { ??? how to test if SetString worked ??? }
  1577.         FailOSerr(memFullErr);
  1578.     ForAllWindowsDo(InstallTitle);
  1579.     END;
  1580.  
  1581. {--------------------------------------------------------------------------------------------------}
  1582. {$S MADocumentRes}
  1583.  
  1584. PROCEDURE TDocument.SetChangeCount(newChangeCount: LONGINT);
  1585.  
  1586. { (??? should we add this as a default action with a TView.DocumentChanged method?)
  1587. You can notify your views that the document changed something like this:
  1588.     PROCEDURE NotifyChange(aView: TView);
  1589.  
  1590.         BEGIN
  1591.         if Member(aView, TMyClass) THEN
  1592.             TMyView(aView).DocumentChanged(newChangeCount);
  1593.         END;
  1594. }
  1595.  
  1596.     BEGIN
  1597.     fChangeCount := newChangeCount;
  1598.     { ForAllViewsDo(NotifyChange); }
  1599.     END;
  1600.  
  1601. {--------------------------------------------------------------------------------------------------}
  1602. {$S MAWriteFile}
  1603.  
  1604. PROCEDURE TDocument.SFPutParms(itsCmdNumber: CmdNumber;
  1605.                                VAR dlgID: INTEGER;
  1606.                                VAR where: Point;
  1607.                                VAR defaultName, prompt: Str255;
  1608.                                VAR dlgHook, filterProc: ProcPtr);
  1609.  
  1610.     VAR
  1611.         dlogTemplate:        DialogTHndl;
  1612.         dialogRect:         Rect;
  1613.         idx:                INTEGER;
  1614.  
  1615.     BEGIN
  1616.     dlgID := putDlgID;                                    {putDlgID defined by Standard File}
  1617.  
  1618.     { compute the top-left location of the dialog }
  1619.     dlogTemplate := DialogTHndl(GetResource('DLOG', dlgID));
  1620.     IF dlogTemplate <> NIL THEN
  1621.         BEGIN
  1622.         dialogRect := dlogTemplate^^.boundsRect;
  1623.         CenterRectOnScreen(dialogRect, TRUE, TRUE, TRUE);
  1624.         where := dialogRect.topleft;
  1625.         END
  1626.     ELSE
  1627.         SetPt(where, 100, 100);
  1628.  
  1629.     CASE itsCmdNumber OF
  1630.         cSave, cSaveAs:
  1631.             idx := bzSaveAs;
  1632.         cSaveCopy:
  1633.             idx := bzSaveCopy;
  1634.         OTHERWISE
  1635.             idx := 0;
  1636.     END;
  1637.  
  1638.     IF idx = 0 THEN
  1639.         prompt := ''
  1640.     ELSE
  1641.         GetIndString(prompt, kIDBuzzString, idx);
  1642.  
  1643.     dlgHook := NIL;
  1644.     filterProc := NIL;
  1645.     END;
  1646.  
  1647. {--------------------------------------------------------------------------------------------------}
  1648. {$S MAReadFile}
  1649.  
  1650. PROCEDURE TDocument.ShowReverted;
  1651.  
  1652.     PROCEDURE RevertView(aView: TView);
  1653.  
  1654.         BEGIN
  1655.         aView.ShowReverted;
  1656.         END;
  1657.  
  1658.     BEGIN
  1659.     ForAllViewsDo(RevertView);
  1660.     END;
  1661.  
  1662. {--------------------------------------------------------------------------------------------------}
  1663. {$S MAOpen}
  1664.  
  1665. PROCEDURE TDocument.ShowWindows;
  1666.  
  1667.     FUNCTION ShowAWindow(aWindow: TWindow): BOOLEAN;
  1668.  
  1669.         BEGIN
  1670.         IF aWindow.fOpenInitially THEN
  1671.             aWindow.Open;
  1672.         ShowAWindow := FALSE;
  1673.         END;
  1674.  
  1675.     BEGIN
  1676.     { Make the windows open from back to front }
  1677.     IF (fWindowList <> NIL) & (fWindowList.LastThat(ShowAWindow) <> NIL) THEN;
  1678.     END;
  1679.  
  1680. {--------------------------------------------------------------------------------------------------}
  1681. {$S MAOpen}
  1682.  
  1683. PROCEDURE TDocument.UntitledName(VAR noName: Str255);
  1684.  
  1685.     VAR
  1686.         preInsert:            INTEGER;
  1687.         constChars:         INTEGER;
  1688.         num:                Str255;
  1689.  
  1690.     BEGIN
  1691.     GetIndString(noName, kIDBuzzString, bzUntitled);
  1692.     IF ParseTitleTemplate(noName, preInsert, constChars) THEN
  1693.         BEGIN
  1694.         NumToString(gNumUntitled, num);
  1695.  
  1696.         IF SubstituteInTitle(noName, num, preInsert, constChars) THEN
  1697.             gNumUntitled := gNumUntitled + 1;
  1698.         END;
  1699.     END;
  1700.  
  1701. {--------------------------------------------------------------------------------------------------}
  1702. {$S MAFields}
  1703.  
  1704. PROCEDURE TDocument.Fields(PROCEDURE DoToField(fieldName: Str255;
  1705.                                                fieldAddr: Ptr;
  1706.                                                fieldType: INTEGER));
  1707.  
  1708.     BEGIN
  1709.     DoToField('TDocument', NIL, bClass);
  1710.     DoToField('fWindowList', @fWindowList, bObject);
  1711.     DoToField('fViewList', @fViewList, bObject);
  1712.     DoToField('fChangeCount', @fChangeCount, bLongInt);
  1713.     DoToField('fDocPrintHandler', @fDocPrintHandler, bObject);
  1714.     DoToField('fSavePrintInfo', @fSavePrintInfo, bBoolean);
  1715.     DoToField('fSharePrintInfo', @fSharePrintInfo, bBoolean);
  1716.     DoToField('fPrintInfo', @fPrintInfo, bHandle);
  1717.     DoToField('fTitle', @fTitle, bStringHandle);
  1718.     DoToField('fFileType', @fFileType, bOSType);
  1719.     DoToField('fCreator', @fCreator, bOSType);
  1720.     DoToField('fVolRefNum', @fVolRefNum, bInteger);
  1721.     DoToField('fModDate', @fModDate, bLongInt);
  1722.     DoToField('fReopenAlert', @fReopenAlert, bBoolean);
  1723.     DoToField('fSaveExists', @fSaveExists, bBoolean);
  1724.     DoToField('fCommitOnSave', @fCommitOnSave, bBoolean);
  1725.     DoToField('fUsesDataFork', @fUsesDataFork, bBoolean);
  1726.     DoToField('fUsesRsrcFork', @fUsesRsrcFork, bBoolean);
  1727.     DoToField('fDataOpen', @fDataOpen, bBoolean);
  1728.     DoToField('fRsrcOpen', @fRsrcOpen, bBoolean);
  1729.     DoToField('fDataPerm', @fDataPerm, bInteger);
  1730.     DoToField('fRsrcPerm', @fRsrcPerm, bInteger);
  1731.     DoToField('fDataRefnum', @fDataRefnum, bInteger);
  1732.     DoToField('fRsrcRefNum', @fRsrcRefnum, bInteger);
  1733.     DoToField('fSaveInPlace', @fSaveInPlace, bByte);
  1734.     INHERITED Fields(DoToField);
  1735.     END;
  1736.